#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <string.h>
#include "cmd.hpp"
#include "in.hpp"
#include "../tools/block.hpp"
#include "../tools/robot.hpp"
#include "../tools/boom.hpp"

void PrintWithColor(Color,std::string);
void PrintHelp();
void PrintStd(const char *str);
void PrintStd(std::string);
void ClearScreen();

void PrintUserMsg(User& user);
void MapDisplay(std::vector<Land>& map);

void CmdCtrl::WaitInput(User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap,std::vector<int>& turn){
	bool flag=true;
	while (flag) {
		//check bankruptcy
		if (user.GetStatus()==Bankruptcy) {
			auto& houses=*user.GetHouseMsg();
			for (auto iter : houses) {
				auto house=map[iter.first].GetHouse();
				ASSERT(house!=nullptr);
				house->Clear();
			}
			if (user.GetPos()!=-1) {
				map[user.GetPos()].Reset();
				user.SetPos(-1);
			}
			houses.clear();
			return;
		}
		if (user.GetStopNum()!=0) {
			PrintWithColor(user.MapColor(),user.GetName()+">"+"暂停ing 剩余"+std::to_string(user.GetStopNum())+"\n");
			map[user.GetPos()].ChangeStatus(user);
			map[user.GetPos()].Reset();
			this->ctrlDice=0;
			Roll(user, map, hashMap);
			MapDisplay(map);
			break;
		}
		PrintWithColor(user.MapColor(),user.GetName()+">");
		auto input=GetStdInput();
		auto pos=input.find_first_of(' ');
		std::string firstStr="";
		firstStr=input.substr(0,pos);
		if (input=="roll") {
			flag=this->Roll(user, map,hashMap);
			if (user.GetId()!=lastId) {
				/* ClearScreen(); */
			}
		}else if (input=="help") {
			PrintHelp();
		}else if (input=="quit"||input=="exit") {
			exit(0);
		}else if (input=="print") {
			Print(user, map, hashMap, turn);
		}else if (firstStr=="sell") {
			flag=Sold(input, user, map, hashMap);
		}else if (firstStr=="step") {
			flag=Step(input, user, map, hashMap);
		}else if (this->command.find(firstStr)!=this->command.end()) {
			flag=command[firstStr](input,user,map,hashMap);
		}else{
			PrintStd("指令出错:"+input);
		}
		//check bankruptcy
		if (user.GetStatus()==Bankruptcy) {
			auto& houses=*user.GetHouseMsg();
			for (auto iter : houses) {
				auto house=map[iter.first].GetHouse();
				ASSERT(house!=nullptr);
				house->Clear();
			}
			houses.clear();
			if (user.GetPos()!=-1) {
				map[user.GetPos()].Reset();
				user.SetPos(-1);
			}
			return;
		}
	}
	lastId=user.GetId();
	user.OneTurn();
}

void TestPrintTurn(User& user,std::vector<int>& turn,std::unordered_map<int,User*>& users){
	int now=user.GetId();
	std::string out="seq ";
	if (users[now]->GetStatus()!=Bankruptcy) {
		out+=users[now]->MapChar();
	}
	int pos=-1;
	for (int i=0; i<turn.size(); i++) {
		if (turn[i]==now) {
			pos=i;
			break;
		}
	}
	ASSERT(pos!=-1);
	pos++;
	while(1){
		if (pos>=turn.size()) {
			pos-=turn.size();
		}
		now=turn[pos];
		if (now==user.GetId()) {
			break;
		}else {
			if (users[now]->GetStatus()!=Bankruptcy) {
				out+=users[now]->MapChar();
			}
		}
		pos++;
	}
	fprintf(stderr, "%s\n",out.c_str());
}

void TestPrintUserMsg(std::unordered_map<int,User*>& users){
	for (auto iter : users) {
		ASSERT(iter.second!=NULL);
		auto& user=*iter.second;
		if (user.GetStatus()==UserStatus::Bankruptcy||user.GetMoney()<0) {
			continue;
		}
		fprintf(stderr, "%c %d %d %d %d %d %d %d %d\n",\
				user.MapChar(),user.GetPos(),user.GetMoney(),user.GetCredit(),user.GetStopNum(),\
				(*user.GetToolMsg())[Boom],(*user.GetToolMsg())[Block],(*user.GetToolMsg())[Robot],user.GetWealth());
	}
}

void TestPrintMapMsg(std::vector<Land>& map,std::unordered_map<int,User*>& users){
	for (int i=0;i<map.size();i++) {
		auto& now=map[i];
		auto& house=*now.GetHouse();
		auto tool=now.GetTools();
		int flag=0;
		if (tool==NULL) {
			flag=0;
		}else {
			/* ASSERT(tool->GetType()!=ToolType::Boom&&tool->GetType()!=ToolType::Block); */
			if (tool->GetType()==ToolType::Boom) {
				flag=1;
			}else {
				flag=2;
			}
		}
		if (house.price!=-1&&(house.ownId!=0||tool!=nullptr)) {
			if (house.ownId!=0) {
				fprintf(stderr, "Building %d %c %c %d\n",i,house.MapChar(),users[house.ownId]->MapChar(),flag);
			}else{
				fprintf(stderr, "Building %d %c %c %d\n",i,house.outCh,'0',flag);
			}
		}
		if (house.price==-1&&tool!=nullptr) {
			fprintf(stderr, "Building %d %d %d %d\n",i,0,0,flag);
		}
	}
}

bool CmdCtrl::Roll(User &user, std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	int step=user.GetDiceNum(this->ctrlDice);
	int old=user.GetPos();
	int nowPos=old;
	this->ctrlDice=-1;

	for (int i=0; i<step; i++) {
		nowPos++;
		if (nowPos>=map.size()) {
			nowPos-=map.size();
		}
		if (map[nowPos].GetTools()!=NULL) {
			auto tool=map[nowPos].GetTools();
			auto flag=tool->PassFunction(user,map,hashMap);

			map[nowPos].DeleteTool();
			map[nowPos].Reset();

			if (flag) {
				nowPos=user.GetPos();
				break;
			}
		}
	}

	auto house=map[nowPos].GetHouse();
	ASSERT(house!=NULL);
	user.SetPos(nowPos);

	int stepNum=user.GetPos()-old;
	if (stepNum<0) {
		stepNum+=map.size();
	}
	PrintStd("前进:"+std::to_string(stepNum)+" 目前在:"+std::to_string(nowPos));
	house->PassFunction(user,map,hashMap);

	map[user.GetPos()].ChangeStatus(user);
	map[old].Reset();
	MapDisplay(map);

	if (user.GetStatus()==Bankruptcy) {
		if (user.GetPos()!=-1) {
			map[user.GetPos()].Reset();
			user.SetPos(-1);
		}
		for (auto iter : *user.GetHouseMsg()) {
			map[iter.first].GetHouse()->Clear();
		}
	}
	return false;
}


bool CmdCtrl::Step(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	int num=-1;
	if (sscanf(input.c_str(),"step %d",&num)<=0||num<=0) {
		PrintStd("指令出错:"+input);
		return true;
	}
	this->ctrlDice=num;
	return this->Roll(user,map,hashMap);
}


bool CmdCtrl::Sold(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	int num=-1;
	if (sscanf(input.c_str(),"sell %d",&num)<=0||num<0||num>=map.size()) {
		PrintStd("指令出错:"+input);
		return true;
	}
	auto& houses=*user.GetHouseMsg();
	if (houses.find(num)==houses.end()) {
		PrintStd("你没有该房产");
		return true;
	}
	ASSERT(map[num].GetHouse()!=nullptr);
	//TODO add money
	auto& house=*map[num].GetHouse();
	user.SetMoney(user.GetMoney()+house.input*2);
	PrintStd("出售成功,获得:"+std::to_string(house.input*2));
	map[num].GetHouse()->Clear();
	PrintStd("目前金钱为"+std::to_string(user.GetMoney()));
	return true;
}

bool CmdCtrl::Preset(const std::string& input,User& userNow,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	char thing[64]={0},name[32]={0};
	int num=-1;
	auto findUser=[&](char ch)->User*{
		User* user;
		switch (ch) {
		case 'a':
			if (hashMap.find(2)==hashMap.end()) {
				PrintStd("格式出错");
				return nullptr;
			}
			user=hashMap[2];
			break;
		case 'q':
			if (hashMap.find(1)==hashMap.end()) {
				PrintStd("格式出错");
				return nullptr;
			}
			user=hashMap[1];
			break;
		case 'j':
			if (hashMap.find(4)==hashMap.end()) {
				PrintStd("格式出错");
				return nullptr;
			}
			user=hashMap[4];
			break;
		case 's':
			if (hashMap.find(3)==hashMap.end()) {
				PrintStd("格式出错");
				return nullptr;
			}
			user=hashMap[3];
			break;
		default:
			PrintStd("格式出错");
			return nullptr;
		}
		return user;
	};
	if (input.find("building-tool")!=input.npos) {
		int level=-1;
		if(sscanf(input.c_str(), "preset building-tool %d%d",&num,&level)<2){
			PrintStd("指令出错:"+input);
			return true;
		}
		if (num<0||num>map.size()||level<0||level>2) {
			PrintStd("指令出错:"+input);
			return true;
		}
		//change house status
		auto house=map[num].GetHouse();
		ToolBase* tool=nullptr;
		switch (level) {
		case 1:
			tool=new class Boom;
			break;
		case 2:
			tool=new class Block;
			break;
		}
		tool->PutFunction(num, userNow, map, hashMap);
		map[num].SetTools(tool);
		map[num].ChangeStatus(*tool);
		return true;
	}
	if (input.find("building")!=input.npos) {
		int level=-1;
		if(sscanf(input.c_str(), "preset building %d%d%31s",&num,&level,name)<3){
			PrintStd("指令出错:"+input);
			return true;
		}
		if (num<0||num>map.size()||level<0||level>3||strlen(name)==0) {
			PrintStd("指令出错:"+input);
			return true;
		}
		User* user=findUser(name[0]);
		if (user==nullptr) {
			PrintStd("格式出错");
			return true;
		}
		//change house status
		auto house=map[num].GetHouse();
		house->status=EmptyStatus(level+1);
		house->ownId=user->GetId();
		house->input=(level+1)*house->price;
		house->color=user->MapColor();
		//add to user
		(*user->GetHouseMsg())[num]=level+1;
		return true;
	}
	if (sscanf(input.c_str(),"preset%63s%31s%d",thing,name,&num)<3) {
		PrintStd("指令出错:"+input);
		return true;
	}
	PrintStd(std::string(thing)+" "+name+" "+std::to_string(num));
	if (strlen(name)==0) {
		PrintStd("格式出错");
		return true;
	}
	auto event=std::string(thing);
	User* user=findUser(name[0]);
	if (user==nullptr) {
		PrintStd("格式出错");
		return true;
	}
	ASSERT(user!=nullptr);
	auto& tool=*user->GetToolMsg();
	if (event=="robot") {
		tool[ToolType::Robot]+=num;
	}else if (event=="barrier") {
		tool[ToolType::Block]+=num;
	}else if (event=="bomb") {
		tool[ToolType::Boom]+=num;
	}else if (event=="god") {
		user->SetWealthNum(num);
	}else if (event=="credit") {
		user->SetCredit(num);
	}else if (event=="pos") {
		if (num>map.size()) {
			PrintStd("格式出错");
			return true;
		}
		map[user->GetPos()].Reset();
		user->SetPos(num);
		map[num].ChangeStatus(*user);
		MapDisplay(map);
	}else if (event=="money") {
		user->SetMoney(num);
	}else if (event=="state") {
		user->SetStopNum(num);
	}else {
		PrintStd("格式出错");
		return true;
	}
	return true;
}

bool CmdCtrl::Print(User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap,std::vector<int>& turn){
	TestPrintTurn(user, turn, hashMap);
	TestPrintUserMsg(hashMap);
	TestPrintMapMsg(map,hashMap);
	return true;
}

void CmdCtrl::PutTool(ToolType type,int pos,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	if ((*user.GetToolMsg())[type]==0) {
		PrintStd("你没有该工具");
		return;
	}
	if (map[pos].GetTools()!=nullptr) {
		PrintStd("地面上已经有道具了");
		return;
	}
	if (type!=ToolType::Robot) {
		for (auto iter : hashMap) {
			if (iter.second->GetPos()==pos) {
				PrintStd(std::to_string(pos)+" 地面上已经有人了 "+std::to_string(iter.second->GetId()));
				return;
			}
		}
		if (pos==HOSPITAL_POS||pos==JAIL_POS) {
			/* PrintStd("监狱/医院不能放道具"); */
			PrintStd("公园不能放道具");
			return;
		}
	}
	ToolBase* tool=nullptr;
	switch (type) {
	case ToolType::Block:
		tool=new class Block;
		break;
	case ToolType::Robot:
		tool=new class Robot;
		break;
	case ToolType::Boom:
		tool=new class Boom;
		break;
	}
	auto& tools=*user.GetToolMsg();
	tools[type]--;
	tool->PutFunction(pos, user, map, hashMap);
	PrintStd("成功");
	if (type!=ToolType::Robot) {
		map[pos].SetTools(tool);
		map[pos].ChangeStatus(*tool);
	}else{
		delete tool;
		tool=nullptr;
	}
	MapDisplay(map);
}

bool CmdCtrl::Block(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	int num=-1;
	if (sscanf(input.c_str(),"block %d",&num)<=0||num>10||num<-10) {
		PrintStd("格式出错");
		return true;
	}
	num+=user.GetPos();
	if (num<0) {
		num+=map.size();
	}
	if (num>=map.size()) {
		num-=map.size();
	}
	CmdCtrl::PutTool(ToolType::Block, num, user, map, hashMap);
	return true;
}
bool CmdCtrl::Bomb(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	int num=-1;
	if (sscanf(input.c_str(),"bomb %d",&num)<=0||num>10||num<-10) {
		PrintStd("格式出错");
		return true;
	}
	num+=user.GetPos();
	if (num<0) {
		num+=map.size();
	}
	if (num>=map.size()) {
		num-=map.size();
	}
	CmdCtrl::PutTool(ToolType::Boom, num, user, map, hashMap);
	return true;
}

bool CmdCtrl::Robot(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	auto pos=user.GetPos();
	if (pos<0) {
		pos+=map.size();
	}
	if (pos>=map.size()) {
		pos-=map.size();
	}
	if (map[pos].GetTools()!=nullptr) {
		PrintStd("地面上已经有道具了");
		return true;
	}
	CmdCtrl::PutTool(ToolType::Robot, pos, user, map, hashMap);
	return true;
}


bool CmdCtrl::Query(const std::string& input,User& user,std::vector<Land>& map,std::unordered_map<int,User*>& hashMap){
	std::string out="money:"+std::to_string(user.GetMoney())+\
					 "\ncredit:"+std::to_string(user.GetCredit())+\
					 "\nrobot:"+std::to_string((*user.GetToolMsg())[ToolType::Robot])+\
					 "\nblock:"+std::to_string((*user.GetToolMsg())[ToolType::Block])+\
					 "\nboom:"+std::to_string((*user.GetToolMsg())[ToolType::Boom])+\
					 "\ngod:"+std::to_string(user.GetWealth())+\
					 "\nstop:"+std::to_string(user.GetStopNum());
	PrintStd(out);
	return true;
}
